package com.hmall.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.hmall.common.dto.PageDTO;
import com.hmall.search.pojo.ItemDoc;
import com.hmall.search.pojo.PageResult;
import com.hmall.search.pojo.RequestParams;
import com.hmall.search.service.SearchService;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;

@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private RestHighLevelClient client;

    // 获取聚合结果
    @Override
    public Map<String, List<String>> getFilters(RequestParams params) {
        SearchRequest request= new SearchRequest("item");
        request.source().size(0);
        extracted(request,params);

        request.source().aggregation(
                AggregationBuilders
                        .terms("brandAgg")
                        .field("brand")
                        .size(20)
        );
        request.source().aggregation(
                AggregationBuilders
                        .terms("categoryAgg")
                        .field("category")
                        .size(20)
        );
        SearchResponse response = null;
        try {
            response = client.search(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        Aggregations aggregations = response.getAggregations();
        Map<String,List<String>> map  = new HashMap<>();
        Terms brandAgg = aggregations.get("brandAgg");
        if(brandAgg!=null){
            List<String> brandList = new ArrayList<>();
            for(Terms.Bucket bucket:brandAgg.getBuckets()){
                brandList.add(bucket.getKeyAsString());
            }
            map.put("brand",brandList);
        }
        Terms categoryAgg = aggregations.get("categoryAgg");
        if(categoryAgg!=null){
            List<String> categoryList = new ArrayList<>();
            for(Terms.Bucket bucket:categoryAgg.getBuckets()){
                categoryList.add(bucket.getKeyAsString());
            }
            map.put("category",categoryList);
        }
        return map;
    }

    // 构建查询条件
    private void extracted(SearchRequest request, RequestParams params) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if(params.getKey()==null || "".equals(params.getKey())){
            boolQueryBuilder.must(QueryBuilders.matchAllQuery());
        }else{
            boolQueryBuilder.must(QueryBuilders.matchQuery("all", params.getKey()));
        }
        if(params.getBrand()!=null && !"".equals(params.getBrand())){
            boolQueryBuilder.filter(QueryBuilders.termQuery("brand", params.getBrand()));
        }
        if(params.getCategory()!=null && !"".equals(params.getCategory())){
            boolQueryBuilder.filter(QueryBuilders.termQuery("category", params.getCategory()));
        }
        if(params.getMinPrice()!=null && params.getMaxPrice()!=null){
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").from(params.getMinPrice()).to(params.getMaxPrice()));
        }
        request.source().query(boolQueryBuilder);

        //进行算分，将广告费给的多的置于前面
        FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders.functionScoreQuery(
                boolQueryBuilder,
                new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                        // 其中的一个function score 元素
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                // 过滤条件
                                QueryBuilders.termQuery("isAD", true),
                                // 算分函数
                                ScoreFunctionBuilders.weightFactorFunction(10)
                        )
                });
        request.source().query(functionScoreQuery);
        // 根据销量，价格排序
        if(params.getSortBy()!=null &&  !"".equals(params.getSortBy())){
            if("sold".equals(params.getSortBy())){
                request.source().sort("sold", SortOrder.DESC);
            }else if ("price".equals(params.getSortBy())){
                request.source().sort("price", SortOrder.ASC);
            }
        }
        //高亮搜索关键字
        request.source().highlighter(
                new HighlightBuilder()
                        .field("name")
                        .requireFieldMatch(false)
                        .preTags("<em>")
                        .postTags("</em>")
        );
    }

    //基本搜索功能，分页
    @Override
    public PageDTO<ItemDoc> search(RequestParams params) {
        int pageNum=params.getPage();
        int pageSize=params.getSize();
        SearchRequest request = new SearchRequest("item");
        //多条件过滤
        extracted(request,params);
        //分页
        request.source().from((pageNum-1)*pageSize).size(pageSize);
        SearchResponse response = null;
        try {
            response = client.search(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return handlePageResult(response);
    }

    //处理分页结果
    private PageDTO handlePageResult(SearchResponse response) {
        long total = response.getHits().getTotalHits().value;
        SearchHit[] hits = response.getHits().getHits();
        List<ItemDoc> hotels = new ArrayList<>();
        for(SearchHit hit : hits){
            String sourceAsString = hit.getSourceAsString();
            ItemDoc hotelDoc = JSON.parseObject(sourceAsString, ItemDoc.class);
            //要在这里进行高亮处理，不然不生效
            if(hit.getHighlightFields().size()>0){
                //获取高亮字段，设置高亮字段。获取该字段的第一个片段并转换为字符串。
                String name = hit.getHighlightFields().get("name").getFragments()[0].string();
                hotelDoc.setName(name);
            }
            hotels.add(hotelDoc);
        }
        return new PageDTO(total,hotels);
    }


}
